package org.sloth.mx.hk
{
	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.events.Event;
	import flash.events.KeyboardEvent;
	import flash.text.TextField;
	
	import mx.core.Application;
	import mx.core.IContainer;
	import mx.events.IndexChangedEvent;

	/**
	 * 热键管理器（需要调用静态方法<code>initialize</code>），需要接收热键的组件只需要实现接口<code>IHKClient</code>即可。
	 * 在整个显示列表中，最上层的热键对象将会被调用（孩子优先于父亲，兄弟之间 <code>getChildIndex</code> 值越大，优先级越高），全局热键对象始终会被调用
	 * 
	 */	
	public class HKMgr
	{
//============================================================================
//PROPERTY
//============================================================================
		private static var _clientDirty:Boolean = true;
		private static var _client:IHKClient;
		private static var _initialized:Boolean = false;
		
		/** 全局热键对象 */
		public static var globalClient:IHKClient;
//============================================================================
//PUBLIC
//============================================================================
		/** 初始化提示热键管理器 */
		public static function initialize():void
		{
			if (_initialized) return;
			_initialized = true;
			
			var app:Application = Application.application as Application;
			if (app.stage == null)
			{
				app.addEventListener(Event.ADDED_TO_STAGE, appAddedToStage_handler);
				return;
			}
			initializeClients();
		}
		/** 调用该函数，标志当前热键对象已失效，在下次调用热键时会更新热键对象 */
		public static function invalidate():void
		{
			_clientDirty = true;
		}
//============================================================================
//PRIVATE
//============================================================================
		private static function initializeClients():void
		{
			var app:Application = Application.application as Application;
			
			app.addEventListener(Event.ACTIVATE, appActivated_handler);
			
			app.stage.addEventListener(Event.ADDED, stageAdded_handler);
			app.stage.addEventListener(Event.REMOVED, stageRemoved_handler);
			
			app.stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDown_handler);
			app.stage.addEventListener(KeyboardEvent.KEY_UP, keyUp_handler);
			
			var index:int;
			var child:DisplayObject;
			var list:Array = [app.stage];
			while (list.length > 0)
			{
				child = list.shift();
				if (child is DisplayObjectContainer)
				{
					index = DisplayObjectContainer(child).numChildren - 1;
					while (index > -1)
					{
						list.push(DisplayObjectContainer(child).getChildAt(index--));
					}
				}
				if (child is IContainer) IContainer(child).addEventListener(IndexChangedEvent.CHILD_INDEX_CHANGE, childIndexChanged_handler);
			}
		}
		private static function getClient():IHKClient
		{
			if (_clientDirty)
			{
				var app:Application = Application.application as Application;
				_client = recursion(app.stage);
				_clientDirty = false;
			}
			return _client;
		}
		private static function recursion(target:DisplayObject):IHKClient
		{
			var client:IHKClient;
			if (target is DisplayObjectContainer)
			{
				var child:DisplayObject;
				var index:int = DisplayObjectContainer(target).numChildren - 1;
				while (index > -1)
				{
					client = recursion(DisplayObjectContainer(target).getChildAt(index--));
					if (client != null) return client;
				}
			}
			if ((target.visible) && (target is IHKClient)) return target as IHKClient;
			return null;
		}
//============================================================================
//PRIVATE
//============================================================================
		private static function appActivated_handler(event:Event):void
		{
			var app:Application = Application.application as Application;
			app.setFocus();
		}
		private static function appAddedToStage_handler(event:Event):void
		{
			var app:Application = Application.application as Application;
			app.removeEventListener(Event.ADDED_TO_STAGE, appAddedToStage_handler);
			
			initializeClients();
		}
		private static function stageAdded_handler(event:Event):void
		{
			var client:IHKClient = recursion(event.target as DisplayObject);
			if (client != null) invalidate();
			if (event.target is IContainer) IContainer(event.target).addEventListener(IndexChangedEvent.CHILD_INDEX_CHANGE, childIndexChanged_handler);
		}
		private static function stageRemoved_handler(event:Event):void
		{
			var client:IHKClient = recursion(event.target as DisplayObject);
			if (client != null) invalidate();
			if (event.target is IContainer) IContainer(event.target).removeEventListener(IndexChangedEvent.CHILD_INDEX_CHANGE, childIndexChanged_handler);
		}
		private static function childIndexChanged_handler(event:IndexChangedEvent):void
		{
			invalidate();
		}
		private static function keyDown_handler(event:KeyboardEvent):void
		{
			var app:Application = Application.application as Application;
			if (app.getFocus() is TextField) return;
			var client:IHKClient = getClient();
			if (client != null) client.keyDown(event);
			if (globalClient != null) globalClient.keyDown(event);
		}
		private static function keyUp_handler(event:KeyboardEvent):void
		{
			var app:Application = Application.application as Application;
			if (app.getFocus() is TextField) return;
			var client:IHKClient = getClient();
			if (client != null) client.keyUp(event);
			if (globalClient != null) globalClient.keyUp(event);
		}
	}
}